package cn.iocoder.yudao.module.trade.service.order;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.currency.api.rate.ExchangeRateApi;
import cn.iocoder.yudao.module.currency.api.rate.dto.ExchangeRateRespDTO;
import cn.iocoder.yudao.module.trade.config.TradeOrderLimitConfig;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
public class OrderLimitCheckService {

    @Resource
    private TradeOrderQueryService tradeOrderQueryService;

    @Resource
    private TradeOrderLimitConfig tradeOrderLimitConfig;

    @Resource
    private ExchangeRateApi exchangeRateApi;

    public void checkOrderLimit(TradeOrderDO order) {
        Integer terminal = order.getTerminal();
        TradeOrderLimitConfig.TerminalConfig defaultConfig = tradeOrderLimitConfig.defaultConfig();
        boolean enable = defaultConfig.getEnable();
        BigDecimal dayAmount = defaultConfig.getDayAmount();
        BigDecimal monthAmount = defaultConfig.getMonthAmount();
        if (Objects.nonNull(terminal)) {
            TerminalEnum terminalEnum = TerminalEnum.fromValue(terminal);
            Map<String, TradeOrderLimitConfig.TerminalConfig> terminalConfig = tradeOrderLimitConfig.getTerminalConfig();
            log.info("[checkOrderLimit][terminal({}), terminalConfigSize({})]", terminalEnum.name(), MapUtils.size(terminalConfig));
            TradeOrderLimitConfig.TerminalConfig config = terminalConfig.get(terminalEnum.name());
            if (Objects.nonNull(config)) {
                enable = config.getEnable();
                dayAmount = config.getDayAmount();
                monthAmount = config.getMonthAmount();
            }
        }
        if (!enable) {
            log.info("[checkOrderLimit][订单编号({}), 未开启订单限额检查]", order.getNo());
            return;
        }
        LocalDateTime now = LocalDateTimeUtil.now();
        // 计算当天已下单金额
        {
            LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(now);
            LocalDateTime endOfDay = LocalDateTimeUtil.endOfDay(now);
            List<TradeOrderDO> orders = tradeOrderQueryService.getOrderListByUserId(order.getUserId(), null,
                    TradeOrderStatusEnum.CANCELED.getStatus(), beginOfDay, endOfDay);
            BigDecimal orderedAmount = calculateOrderAmount(orders);
            BigDecimal currentAmount = calculateOrderAmount(order);
            log.info("[checkOrderLimit][订单编号({}), 已下单金额({})元, 当前下单金额({})元, 当日限额({})元]", order.getNo(), orderedAmount, currentAmount, dayAmount);
            // 剩余额度
            BigDecimal surplus = dayAmount.subtract(orderedAmount);
            if (currentAmount.compareTo(surplus) > 0) {
                log.error("[checkOrderLimit][订单编号({}), 当前下单金额({})元, 剩余额度({})元, 日内超限]", order.getNo(), currentAmount, surplus);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_DAY_AMOUNT_OVER_LIMIT, dayAmount);
            }
        }
        // 计算当月已下单金额
        {
            LocalDateTime beginOfMonth = LocalDateTimeUtils.beginOfMonth(now);
            LocalDateTime endOfMonth = LocalDateTimeUtils.endOfMonth(now);
            List<TradeOrderDO> orders = tradeOrderQueryService.getOrderListByUserId(order.getUserId(), null,
                    TradeOrderStatusEnum.CANCELED.getStatus(), beginOfMonth, endOfMonth);
            BigDecimal orderedAmount = calculateOrderAmount(orders);
            BigDecimal currentAmount = calculateOrderAmount(order);
            log.info("[checkOrderLimit][订单编号({}), 已下单金额({})元, 当前下单金额({})元, 当月限额({})元]", order.getNo(), orderedAmount, currentAmount, monthAmount);
            BigDecimal surplus = monthAmount.subtract(orderedAmount);
            if (currentAmount.compareTo(surplus) > 0) {
                log.error("[checkOrderLimit][订单编号({}), 当前下单金额({})元, 剩余额度({})元, 当月超限]", order.getNo(), currentAmount, surplus);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_MONTH_AMOUNT_OVER_LIMIT, monthAmount);
            }
        }
    }

    public BigDecimal calculateOrderAmount(List<TradeOrderDO> orders) {
        BigDecimal totalAmount = BigDecimal.ZERO;
        if (CollectionUtil.isNotEmpty(orders)) {
            for (TradeOrderDO order : orders) {
                totalAmount = totalAmount.add(calculateOrderAmount(order));
            }
        }
        return totalAmount;
    }

    public BigDecimal calculateOrderAmount(TradeOrderDO order) {
        if (Objects.isNull(order)) {
            return BigDecimal.ZERO;
        }
        Integer payPrice = order.getPayPrice();
        Integer refundPrice = order.getRefundPrice();
        if (Objects.isNull(refundPrice)) {
            refundPrice = 0;
        }
        int orderPrice = payPrice - refundPrice;
        String orderCurrency = order.getOrderCurrency();
        String limitCurrency = tradeOrderLimitConfig.getCurrency(order.getTerminal());
        // 订单币种与限额币种一致
        if (StringUtils.equals(orderCurrency, limitCurrency)) {
            return BigDecimal.valueOf(orderPrice).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
        } else {
            // 订单币种与限额币种不一致, 先转换成支付币种
            Integer paymentPrice;
            if (payPrice.equals(orderPrice)) {
                paymentPrice = order.getPaymentPrice();
            } else {
                paymentPrice = exchangeRateApi.calculateWithRate(orderPrice, order.getExchangeRate());
            }
            String paymentCurrency = order.getPaymentCurrency();
            BigDecimal rate;
            ExchangeRateRespDTO exchangeRate = exchangeRateApi.getExchangeRate(paymentCurrency, limitCurrency);
            if (Objects.isNull(exchangeRate)) {
                // 找不到支付币种与限额币种的汇率, 反向查询
                exchangeRate = exchangeRateApi.getExchangeRate(limitCurrency, paymentCurrency);
                if (Objects.isNull(exchangeRate)) {
                    log.error("订单限额校验时, 没有找到汇率信息, 用于计算用户已下单金额, 请补充汇率信息{}->{}", paymentCurrency, limitCurrency);
                    throw new IllegalArgumentException("找不到汇率信息");
                } else {
                    rate = exchangeRateApi.reverseRate(exchangeRate.getExchangeRate());
                }
            } else {
                rate = exchangeRate.getExchangeRate();
            }
            return rate.multiply(BigDecimal.valueOf(paymentPrice)).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
        }
    }

}
